package gek.uni.diplom.ocl.example.generators.visitors;

import gek.uni.diplom.ocl.example.exceptions.ErrorException;
import gek.uni.diplom.ocl.example.exceptions.NotImplementedMethodException;
import gek.uni.diplom.ocl.example.managers.ErrorManager;
import gek.uni.diplom.ocl.example.patterns.Pattern;
import gek.uni.diplom.ocl.example.utils.GeneratorUtils;
import gek.uni.diplom.ocl.example.utils.LogManager;
import gek.uni.diplom.ocl.example.utils.OclEcoreUtils;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.ocl.OCL;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.EcoreEnvironmentFactory;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.ecore.internal.OCLStandardLibraryImpl;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.expressions.VariableExp;
import org.eclipse.ocl.types.PrimitiveType;
import org.eclipse.ocl.utilities.AbstractVisitor;
import org.eclipse.ocl.utilities.PredefinedType;

/**
 * Все же нужно генерировать и для истинного утверждения и для ложного. <br/>
 * ((a+b) = 3 = false)
 * 
 * 
 * @author Gek
 * 
 */
@SuppressWarnings("restriction")
public class PatternGeneratorVisitor
    extends
    AbstractVisitor<Pattern, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> {

  public static String REPLACE_CODE_STRING = "@INSERT_CODE";

  OCL<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> ocl;

  public PatternGeneratorVisitor() {
    ocl = OCL.newInstance(EcoreEnvironmentFactory.INSTANCE);
  }

  @Override
  public VariableExpCodeNode visitVariableExp(VariableExp<EClassifier, EParameter> v) {
    EClassifier type = v.getType();
    String varName = v.getName();

    VariableExpCodeNode ptrn = new VariableExpCodeNode();

    String[] declarationStrings =
        OclEcoreUtils.createVariableDeclarationString(type, varName);
    // String declaration = declarationStrings[0];
    // String initialization = declarationStrings[1];
    // ptrn.addDeclarationSection(varName, declaration);
    // ptrn.addInitializationSection(varName, initialization);
    for (int i = 2; i < declarationStrings.length; i++) {
      ptrn.addImportString(declarationStrings[i]);
    }
    ptrn.getDeclaredVariableNames().add(varName);
    // ptrn.setReference(varName, varName);

    String[] variableGetterStrings = createVariableGetterStrings(v, ptrn);
    String[] variableSetterStrings = createVariableSetterStrings(v, ptrn);

    try {
      ptrn.setSetterInitString(v.toString(), variableSetterStrings[0]);
    } catch (ErrorException e) {
      LogManager.logError(e.getMessage());
    }
    try {
      ptrn.setSetterCallString(v.toString(), variableSetterStrings[1]);
    } catch (ErrorException e) {
      LogManager.logError(e.getMessage());
    }
    try {
      ptrn.setGetterInitString(v.toString(), variableGetterStrings[0]);
    } catch (ErrorException e) {
      LogManager.logError(e.getMessage());
    }
    try {
      ptrn.setGetterCallString(v.toString(), variableGetterStrings[1]);
    } catch (ErrorException e) {
      LogManager.logError(e.getMessage());
    }
    return ptrn;
  }

  @Override
  protected VariableExpCodeNode handleOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) {
    sourceResult.getGetterInitString(callExp.toString());
    sourceResult.getSetterInitString(callExp.toString());
    OclEcoreUtils.copyPatternInitSections(sourceResult, callExp.getSource()
        .toString(), callExp.toString());
    // TODO добавить строки инициализации переменных из правой части

    EClassifier typeOfExp = callExp.getType();
    if (typeOfExp.equals(OCLStandardLibraryImpl.INSTANCE.getBoolean()))
      /* && !OclEcoreUtils.checkIsLeafExp(callExp) */
      try {
        return handleBooleanOperationCallExp(callExp, sourceResult,
            argumentResults);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());// e.printStackTrace();
      } catch (NotImplementedMethodException e) {
        ErrorManager.addError(e.getMessage());// e.printStackTrace();
      }
    else if (typeOfExp.equals(OCLStandardLibraryImpl.INSTANCE.getInteger())) {
      return handleIntegerOperationCallExp(callExp, sourceResult,
          argumentResults);
    }

    OCLExpression<EClassifier> source = callExp.getSource();

    int opCode = callExp.getOperationCode();

    VariableExpCodeNode result = sourceResult;
    switch (opCode) {
    case PredefinedType.EQUAL:
      if (source.getType().equals(OCLStandardLibraryImpl.INSTANCE.getBoolean())) {
        Boolean expectedValue =
            (Boolean) OclEcoreUtils.evaluateExpression(callExp.getArgument()
                .get(0), ocl);
        // ((BooleanLiteralExp<EClassifier>) callExp.getArgument().get(0))
        // .getBooleanSymbol();
        if (expectedValue) {
          result.removeFalseInitialization(source.toString());
        } else
          result.removeTrueInitialization(source.toString());
      }

    default:
      break;
    }
    return result;
  }

  @Override
  protected VariableExpCodeNode handlePropertyCallExp(
      PropertyCallExp<EClassifier, EStructuralFeature> callExp,
      VariableExpCodeNode sourceResult, List<VariableExpCodeNode> qualifierResults) {

    VariableExpCodeNode result = sourceResult;
    OCLExpression<EClassifier> source = callExp.getSource();

    EStructuralFeature property = callExp.getReferredProperty();

    String[] propertyGetterStrings =
        generateProperyGetterStrings(source, property, result);

    String exprStr = callExp.toString();
    try {
      // StringBuilder propertyGetterInit =
      // new StringBuilder(result.getGetterInitString(source.toString()));
      // propertyGetterInit.append("\n").append(propertyGetterStrings[1]);
      result.setGetterInitString(exprStr, propertyGetterStrings[1]);
    } catch (ErrorException e) {
      ErrorManager.addError(e.getMessage());
    }
    try {
      // StringBuilder propertyGetterCall =
      // new StringBuilder(result.getGetterCallString(source.toString()));
      // propertyGetterCall.append("\n").append(propertyGetterStrings[2]);
      // result.setReference(exprStr, propertyGetterCall.toString());
      result.setGetterCallString(exprStr, propertyGetterStrings[2]);
    } catch (ErrorException e) {
      ErrorManager.addError(e.getMessage());
    }

    String[] propertySetterStrings =
        generatePropertySetterStrings(callExp, property, result,
            propertyGetterStrings[0]);

    try {
      StringBuilder propertySetterInit =
          new StringBuilder(result.getSetterInitString(source.toString()));
      propertySetterInit.append("\n").append(propertySetterStrings[0]);
      result.setSetterInitString(exprStr, propertySetterInit.toString());
    } catch (ErrorException e) {
      ErrorManager.addError(e.getMessage());
    }
    try {
      // StringBuilder propertySetterCall =
      // new StringBuilder(result.getSetterCallString(source.toString()));
      // propertySetterCall.append("\n").append(propertySetterStrings[1]);
      result.setSetterCallString(exprStr, propertySetterStrings[1]);
    } catch (ErrorException e) {
      ErrorManager.addError(e.getMessage());
    }

    return result;
  }

  // ==========================================================================

  private VariableExpCodeNode handleBooleanOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws ErrorException,
      NotImplementedMethodException {

    // Результат работы добавим с уже созданному результату
    VariableExpCodeNode result = sourceResult;

    // Добавляем строки кода, импортирующие необходимые классы для вычислеия
    // правой части выражения
    if (argumentResults.size() == 0) for (VariableExpCodeNode argPattern : argumentResults)
      result.addImportStrings(argPattern.getImportStrings());

    int opCode = callExp.getOperationCode();

    // Определяем код операции и генерируем 2 варианта инициализации переменной:
    // 1 - Чтобы опрерация возвращала true
    // 2 - Чтобы опреация возвращала false
    switch (opCode) {
    case PredefinedType.OCL_IS_UNDEFINED:
      return handleOclIsUndefinedExpression(callExp, sourceResult,
          argumentResults);
    case PredefinedType.EQUAL:
      return handleEqualOperationCallExp(callExp, sourceResult, argumentResults);
    case PredefinedType.LESS_THAN:
      return handleOclLessCallExp(callExp, sourceResult, argumentResults);
    case PredefinedType.LESS_THAN_EQUAL:
      return handleOclLessThanEqualCallExp(callExp, sourceResult,
          argumentResults);
    default:
      break;
    }

    return result;
  }

  /**
   * Генерация шаблонов кода для операций, которые возвращают Integer:<br/>
   * size(), count(),....
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private VariableExpCodeNode handleIntegerOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) {
    int opCode = callExp.getOperationCode();
    OCLExpression<EClassifier> source = callExp.getSource();
    switch (opCode) {
    case PredefinedType.SIZE:
      return handleSizeOperationCallExp(callExp, sourceResult, argumentResults);
    default:
      break;
    }
    return null;
  }

  /**
   * Генерация шаблонов кода для операции size()
   * 
   * @param callExp
   *          - операция size()
   * @param sourceResult
   * @param argumentResults
   * @return
   */
  private VariableExpCodeNode handleSizeOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) {
    VariableExpCodeNode result = sourceResult;
    OCLExpression<EClassifier> source = callExp.getSource();
    EClassifier sourceType = source.getType();
    if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getSequence())) {
      // TODO Генерация кода для списка
    } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getSet())) {
      // TODO Генерация кода для множества
    } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE
        .getOrderedSet())) {
      // TODO Генерация кода для упорядоченного множества
    } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getBag())) {
      // TODO Генерация кода для упорядоченного упорядоченного списка
    } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getString())) {
      // TODO Генерация кода для строки
    }

    return result;
  }

  /**
   * Обработка выражения "Меньше или равно" для всех возможных типов аргументов
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws NotImplementedMethodException
   * @throws ErrorException
   */
  @SuppressWarnings("unchecked")
  private VariableExpCodeNode handleOclLessThanEqualCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws NotImplementedMethodException,
      ErrorException {
    OCLExpression<EClassifier> source = callExp.getSource();
    EClassifier sourceType = source.getType();
    if (sourceType instanceof PrimitiveType) {
      if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getString())) {
        // TODO обработка выражения "меньше или равно" для типа String
        throw new NotImplementedMethodException(
            "Не реализован метод \"Меньше чем или равно\" для выражений типа String");
      } else if (sourceType
          .equals(OCLStandardLibraryImpl.INSTANCE.getInteger())) {
        return handleLessThanEqualIntegerOperationCallExp(callExp,
            sourceResult, argumentResults);
      } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getReal())) {
        return handleLessThanEqualRealOperationCallExp(callExp, sourceResult,
            argumentResults);
      } else
        throw new NotImplementedMethodException(
            "Не реализован разбор выражения " + callExp.toString()
                + ". Тип аргумента=" + sourceType.toString());
    } else { // Операнды не примитивноготипа
      // TODO обработка выражения "Меньше или равно" для аргументов не
      // примитивного типа (коллекции)
      OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
      VariableExpCodeNode argumentResult = argumentResults.get(0);
      String argGetterCall = argumentResult.getGetterCallString(arg.toString());
      String sourceSetterCall =
          sourceResult.getSetterCallString(source.toString());
      // Генерируем и добавляем в результат такой код, чтобы результат
      // операции был равен true
      String trueString =
          sourceSetterCall.replace(REPLACE_CODE_STRING, argGetterCall);
      try {
        result.setTrueInitialization(callExp.toString(), trueString);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
      // Генерируем и добавляем в результат такой код, чтобы результат
      // операции был равен false
      String falseString =
          sourceSetterCall.replace(REPLACE_CODE_STRING, "null");
      try {
        result.setFalseInitialization(callExp.toString(), falseString);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }

      // Добавляем строки кода, инициализирующие переменные для корректной
      // работы сгенерированного кода
      StringBuilder initCodeString =
          new StringBuilder(sourceResult.getSetterInitString(source.toString()));
      initCodeString.append(argumentResult.getGetterInitString(arg.toString()));
      result.setOperationInitString(callExp.toString(), initCodeString
          .toString());

      // Добавляем сгенерированный код из результата аргумента в
      // результирующий шаблон
      try {
        result.setGetterInitString(arg.toString(), argumentResult
            .getGetterInitString(arg.toString()));
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
      try {
        result.setGetterCallString(arg.toString(), argGetterCall);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
      return result;
    }
  }

  private VariableExpCodeNode handleLessThanEqualRealOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws ErrorException {
    VariableExpCodeNode result = sourceResult;

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Double argVal = (Double) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String trueSetterCall = result.getSetterCallString(source.toString());
    // Первый - такой, чтобы результат выражения саllExp был равен true
    trueSetterCall.replace(REPLACE_CODE_STRING, String.valueOf(argVal - 1.0));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        trueSetterCall.replace(REPLACE_CODE_STRING, String
            .valueOf(argVal + 1.0));
    result.setTrueInitialization(callExp.toString(), trueSetterCall);
    result.setFalseInitialization(callExp.toString(), falseSetterCall);
    return result;
  }

  private VariableExpCodeNode handleLessThanEqualIntegerOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws ErrorException {
    VariableExpCodeNode result = sourceResult;

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Integer argVal = (Integer) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String trueSetterCall = result.getSetterCallString(source.toString());
    // Первый - такой, чтобы результат выражения саllExp был равен true
    trueSetterCall.replace(REPLACE_CODE_STRING, String.valueOf(argVal - 1));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        trueSetterCall.replace(REPLACE_CODE_STRING, String.valueOf(argVal + 1));
    result.setTrueInitialization(callExp.toString(), trueSetterCall);
    result.setFalseInitialization(callExp.toString(), falseSetterCall);
    return result;
  }

  /**
   * Обработка выражения "Меньше или равно" для всех возможных типов аргументов
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws NotImplementedMethodException
   * @throws ErrorException
   */
  @SuppressWarnings("unchecked")
  private VariableExpCodeNode handleOclLessCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws NotImplementedMethodException,
      ErrorException {
    OCLExpression<EClassifier> source = callExp.getSource();
    EClassifier sourceType = source.getType();
    if (sourceType instanceof PrimitiveType) {
      if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getString())) {
        // TODO обработка выражения "Меньше" для типа String
        throw new NotImplementedMethodException(
            "Не реализован метод \"Меньше чем\" для выражений типа String");
      } else if (sourceType
          .equals(OCLStandardLibraryImpl.INSTANCE.getInteger())) {
        return handleLessIntegerOperationCallExp(callExp, sourceResult,
            argumentResults);
      } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getReal())) {
        return handleLessRealOperationCallExp(callExp, sourceResult,
            argumentResults);
      } else
        throw new NotImplementedMethodException(
            "Не реализован разбор выражения " + callExp.toString()
                + ". Тип аргумента=" + sourceType.toString());
      // TODO обаботка выржаения "равно" для примитивных типов
    } else { // Операнды не примитивноготипа
      OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
      VariableExpCodeNode argumentResult = argumentResults.get(0);
      String argGetterCall = argumentResult.getGetterCallString(arg.toString());
      String sourceSetterCall =
          sourceResult.getSetterCallString(source.toString());
      // Генерируем и добавляем в результат такой код, чтобы результат
      // операции был равен true
      String trueString =
          sourceSetterCall.replace(REPLACE_CODE_STRING, argGetterCall);
      try {
        result.setTrueInitialization(callExp.toString(), trueString);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
      // Генерируем и добавляем в результат такой код, чтобы результат
      // операции был равен false
      String falseString =
          sourceSetterCall.replace(REPLACE_CODE_STRING, "null");
      try {
        result.setFalseInitialization(callExp.toString(), falseString);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }

      // Добавляем строки кода, инициализирующие переменные для корректной
      // работы сгенерированного кода
      StringBuilder initCodeString =
          new StringBuilder(sourceResult.getSetterInitString(source.toString()));
      initCodeString.append(argumentResult.getGetterInitString(arg.toString()));
      result.setOperationInitString(callExp.toString(), initCodeString
          .toString());

      // Добавляем сгенерированный код из результата аргумента в
      // результирующий шаблон
      try {
        result.setGetterInitString(arg.toString(), argumentResult
            .getGetterInitString(arg.toString()));
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
      try {
        result.setGetterCallString(arg.toString(), argGetterCall);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
      return result;
    }
  }

  /**
   * Обработка выржаения "Меньше" для аргумнтов типа Real
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException
   */
  private VariableExpCodeNode handleLessRealOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws ErrorException {
    VariableExpCodeNode result = sourceResult;

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Double argVal = (Double) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String trueSetterCall = result.getSetterCallString(source.toString());
    // Первый - такой, чтобы результат выражения саllExp был равен true
    trueSetterCall.replace(REPLACE_CODE_STRING, String.valueOf(argVal - 1));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        trueSetterCall.replace(REPLACE_CODE_STRING, String
            .valueOf(argVal + 1.0));
    result.setTrueInitialization(callExp.toString(), trueSetterCall);
    result.setFalseInitialization(callExp.toString(), falseSetterCall);
    return result;
  }

  /**
   * Обработка выражения "Меньше" для аргументов типа Integer
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException
   */
  private VariableExpCodeNode handleLessIntegerOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws ErrorException {
    VariableExpCodeNode result = sourceResult;

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Integer argVal = (Integer) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String setterCall = result.getSetterCallString(source.toString());
    // Первый - такой, чтобы результат выражения саllExp был равен true
    String trueSetterCall =
        setterCall.replace(REPLACE_CODE_STRING, String.valueOf(argVal - 1));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        setterCall.replace(REPLACE_CODE_STRING, String.valueOf(argVal + 1));
    result.setTrueInitialization(callExp.toString(), trueSetterCall);
    result.setFalseInitialization(callExp.toString(), falseSetterCall);
    return result;
  }

  private VariableExpCodeNode handleOclIsUndefinedExpression(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws ErrorException {
    VariableExpCodeNode result = sourceResult;

    OCLExpression<EClassifier> source = callExp.getSource();

    String setterInitString = result.getSetterInitString(source.toString());
    result.setSetterInitString(callExp.toString(), setterInitString);
    // Получаем строку с именем переменной, в которой накапливается результат
    // source
    String referenceString =
        sourceResult.getSetterCallString(source.toString());// getReference(source.toString());
    if (referenceString == null) {
      throw new ErrorException(
          "Невозможно выполнить генерацию года для выражения " + callExp
              + ". Не найдена строка для ссылки на объект " + source.toString());
    }
    // Генерируем такой код, чтобы операция возвращала true
    try {
      String trueInitString =
          referenceString.replace(REPLACE_CODE_STRING, "null");
      result.setTrueInitialization(callExp.toString(), trueInitString);
    } catch (ErrorException e) {
      LogManager.logError(e.getMessage());
    }

    // Генерируем такой код, чтобы операция возвращала false
    // Т.е создаем не-null объект
    try {
      String nonNullObjectString =
          OclEcoreUtils.createNonNullObjectInitializationString(source
              .getType());
      String falseInitString =
          referenceString.replace(REPLACE_CODE_STRING, nonNullObjectString);
      result.setFalseInitialization(callExp.toString(), falseInitString);
    } catch (ErrorException e) {
      LogManager.logError(e.getMessage());
    }

    // String sourceInitString = result.getSetterInitString(source.toString());
    // result.setSetterInitString(source.toString(), sourceInitString);
    return result;
  }

  @SuppressWarnings( { "unchecked" })
  private VariableExpCodeNode handleEqualOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws ErrorException,
      NotImplementedMethodException {

    VariableExpCodeNode result = sourceResult;
    OCLExpression<EClassifier> source = callExp.getSource();

    String setterInitString = result.getSetterInitString(source.toString());

    EClassifier sourceType = source.getType();
    if (sourceType instanceof PrimitiveType) {
      if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getString())) {
        // TODO обработка выражения "Равно" для типа String
        throw new NotImplementedMethodException(
            "Не реализован метод \"Равно\" для выражений типа String");
      } else if (sourceType
          .equals(OCLStandardLibraryImpl.INSTANCE.getInteger())) {
        return handleEqualIntegerOperationCallExp(callExp, result,
            argumentResults);
      } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getReal())) {
        return handleEqualRealOperationCallExp(callExp, result, argumentResults);
      } else if (sourceType
          .equals(OCLStandardLibraryImpl.INSTANCE.getBoolean())) {
        return handleEqualBooleanOperationCallExp(callExp, result,
            argumentResults);
      } else
        throw new NotImplementedMethodException(
            "Не реализован разбор выражения " + callExp.toString()
                + ". Тип аргумента=" + sourceType.toString());
    } else { // Операнды не примитивноготипа
      OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
      VariableExpCodeNode argumentResult = argumentResults.get(0);
      String argGetterCall = argumentResult.getGetterCallString(arg.toString());
      String sourceSetterCall =
          sourceResult.getSetterCallString(source.toString());
      // Генерируем и добавляем в результат такой код, чтобы результат
      // операции был равен true
      String trueString =
          sourceSetterCall.replace(REPLACE_CODE_STRING, argGetterCall);
      try {
        result.setTrueInitialization(callExp.toString(), trueString);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
      // Генерируем и добавляем в результат такой код, чтобы результат
      // операции был равен false
      String falseString =
          sourceSetterCall.replace(REPLACE_CODE_STRING, "null");
      try {
        result.setFalseInitialization(callExp.toString(), falseString);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }

      // Добавляем строки кода, инициализирующие переменные для корректной
      // работы сгенерированного кода
      StringBuilder initCodeString =
          new StringBuilder(sourceResult.getSetterInitString(source.toString()));
      initCodeString.append(argumentResult.getGetterInitString(arg.toString()));
      try {
        result.setSetterInitString(callExp.toString(), initCodeString
            .toString());
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
      // result.setOperationInitString(callExp.toString(),
      // initCodeString.toString());

      // Добавляем сгенерированный код из результата аргумента в
      // результирующий шаблон
      try {
        result.setGetterInitString(arg.toString(), argumentResult
            .getGetterInitString(arg.toString()));
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
      try {
        result.setGetterCallString(arg.toString(), argGetterCall);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }

      return result;
    }
  }

  /**
   * Обработка выражения "Равно" для типа Integer
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException
   */
  private VariableExpCodeNode handleEqualIntegerOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws ErrorException {

    VariableExpCodeNode result = sourceResult;

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Integer argVal = (Integer) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String setterCall = result.getSetterCallString(source.toString());
    // Первый - такой, чтобы результат выражения саllExp был равен true
    String trueSetterCall =
        setterCall.replace(REPLACE_CODE_STRING, String.valueOf(argVal));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        setterCall.replace(REPLACE_CODE_STRING, String.valueOf(argVal + 1));

    // result.setSetterInitString(source.toString(), propertySetterInitString)
    result.setTrueInitialization(callExp.toString(), trueSetterCall);
    result.setFalseInitialization(callExp.toString(), falseSetterCall);
    return result;
  }

  /**
   * Обработка выражения "Равно" для типа Integer
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException
   */
  private VariableExpCodeNode handleEqualRealOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws ErrorException {

    VariableExpCodeNode result = sourceResult;

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Double argVal = (Double) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String trueSetterCall = result.getSetterCallString(source.toString());
    // Первый - такой, чтобы результат выражения саllExp был равен true
    trueSetterCall.replace(REPLACE_CODE_STRING, String.valueOf(argVal));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        trueSetterCall.replace(REPLACE_CODE_STRING, String
            .valueOf(argVal + 1.0));
    result.setTrueInitialization(callExp.toString(), trueSetterCall);
    result.setFalseInitialization(callExp.toString(), falseSetterCall);
    return result;
  }

  /**
   * Обработка выражения "Равно" для типа Integer
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException
   */
  private VariableExpCodeNode handleEqualBooleanOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, VariableExpCodeNode sourceResult,
      List<VariableExpCodeNode> argumentResults) throws ErrorException {

    VariableExpCodeNode result = sourceResult;

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Boolean argVal = (Boolean) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String trueSetterCall = result.getTrueInitialization(source.toString());
    String falseSetterCall = result.getFalseInitialization(source.toString());
    if (argVal) {
      try {
        result.setTrueInitialization(callExp.toString(), trueSetterCall);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
      try {
        result.setFalseInitialization(callExp.toString(), falseSetterCall);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
    } else {
      try {
        result.setTrueInitialization(callExp.toString(), falseSetterCall);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
      try {
        result.setFalseInitialization(callExp.toString(), trueSetterCall);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());
      }
    }
    // OclEcoreUtils.copyPatterInitSections(result, source.toString(), callExp
    // .toString());
    return result;
  }

  /**
   * Генерирует 3 и более строк и возвращает массив, содержащий их.<br/>
   * В первой строке содержится имя свойства, по которому будем обращаться в
   * коде, во второй строке - код, иницилизирующий переменные, необходимые для
   * корректной работы get-метода<br/>
   * В третьей строке содержится код, вызывающий get-метод.<br/>
   * В третьей и далее строках содержатся строки, описывающие импортируемые
   * классы<br/>
   * <br/>
   * Пример генерируемого кода:<br/>
   * 0:<br/>
   * EClass classifier = (EClass)<br/>
   * StackPackage.eINSTANCE.getEClassifier("Stack"); <br/>
   * EStructuralFeature feature = classifier.getEStructuralFeature("children");<br/>
   * Stack stack = null;<br/>
   * <br/>
   * 1:<br/>
   * stack.eGet(feature);
   * 
   * @param source
   *          - объект, get-метод которого генерируется
   * @param property
   *          - свойство, которое возвращается с помощью вызова get-метода
   * @param pattern
   *          - накапливающий шаблон
   * @return массив из сгенерированных строк
   */
  private String[] generateProperyGetterStrings(
      OCLExpression<EClassifier> source, EStructuralFeature property,
      final VariableExpCodeNode pattern) {

    List<String> importedStrings = new ArrayList<String>();

    EClassifier sourceClassifier = source.getType();
    String reference = pattern.getGetterCallString(source.toString());// pattern.getReference(source.toString());

    importedStrings.add(new String("import ")
        + sourceClassifier.getEPackage().getClass().getName() + ";");
    importedStrings.add(new String("import ")
        + sourceClassifier.getInstanceClassName() + ";");
    importedStrings.add(new String("import "
        + EStructuralFeature.class.getName())
        + ";");

    String classifierName = GeneratorUtils.getUniqueName("classifier");
    String featureName = GeneratorUtils.getUniqueName("feature");
    String packageName = sourceClassifier.getEPackage().getName() + "Package";
    String className = sourceClassifier.getName();
    String propertyName = property.getName();

    // Генерируем строку для инициализации геттера
    StringBuilder getterInitString = new StringBuilder();
    getterInitString.append("EClass ").append(classifierName).append(" = ")
        .append("(EClass)").append(packageName).append(".eINSTANCE").append(
            ".getEClassifier(").append("\"").append(className).append("\")")
        .append(";").append("\n");

    getterInitString.append("EStructuralFeature ").append(featureName).append(
        " = ").append(classifierName).append(".").append(
        "getEStructuralFeature(\"").append(propertyName).append("\")").append(
        ";\n");

    // Генерируем строку для обращения к свойству. Вместе с кастованием в
    // правильный тип
    StringBuilder getterReferenceString =
        new StringBuilder().append("((").append(className).append(")").append(
            reference).append(".eGet(").append(featureName).append("))");

    String[] result = new String[importedStrings.size() + 3];
    result[0] = featureName;
    result[1] = getterInitString.toString();
    result[2] = getterReferenceString.toString();
    int k = 3;
    for (int i = 0; i < importedStrings.size(); i++) {
      result[i + k] = importedStrings.get(i);
    }
    return result;

    // ========= declaration: Пример кода
    /*
     * EClass classifier = (EClass)
     * StackPackage.eINSTANCE.getEClassifier("Stack"); EStructuralFeature
     * feature = classifier.getEStructuralFeature("children"); Stack stack =
     * null; stack.eGet(feature);
     * 
     * feature.setEType(StackPackage.eINSTANCE.getStack());
     * feature.setName("stack"); StackFactory.eINSTANCE.eGet(feature);
     */// --------- declaration
  }

  /**
   * Генерирует 2 строки и возвращает массив, содержащий их.<br/>
   * В первой строке содержится код, иницилизирующий переменные, необходимые для
   * корректной работы set-метода<br/>
   * Во второй строке содержится код, вызывающий set-метод.<br/>
   * <br/>
   * Пример генерируемого кода:<br/>
   * 0:<br/>
   * EClass classifier = (EClass)<br/>
   * StackPackage.eINSTANCE.getEClassifier("Stack"); <br/>
   * EStructuralFeature feature = classifier.getEStructuralFeature("children");<br/>
   * Stack stack = null;<br/>
   * <br/>
   * 1:<br/>
   * stack.eSet(feature, @INSERT_CODE);
   * 
   * @param source
   *          - объект, get-метод которого генерируется
   * @param property
   *          - свойство, которое возвращается с помощью вызова get-метода
   * @param pattern
   *          - накапливающий шаблон
   * @return массив из сгенерированных строк
   */
  private String[] generatePropertySetterStrings(
      PropertyCallExp<EClassifier, EStructuralFeature> callExp,
      EStructuralFeature property, final VariableExpCodeNode pattern, String featureName) {

    OCLExpression<EClassifier> source = callExp.getSource();

    EClassifier sourceClassifier = source.getType();
    String reference = pattern.getGetterCallString(source.toString());// pattern.getReference(source.toString());

    List<String> importedStrings = new ArrayList<String>();
    importedStrings.add(new String("import ")
        + sourceClassifier.getEPackage().getClass().getName() + ";");
    importedStrings.add(new String("import ")
        + sourceClassifier.getInstanceClassName() + ";");
    importedStrings.add(new String("import "
        + EStructuralFeature.class.getName())
        + ";");

    String classifierName = GeneratorUtils.getUniqueName("classifier");
    featureName =
        featureName != null ? featureName : GeneratorUtils
            .getUniqueName("feature");
    String packageName = sourceClassifier.getEPackage().getName() + "Package";
    String className = sourceClassifier.getName();
    String propertyName = property.getName();

    // Генерируем строку для инициализации геттера
    StringBuilder setterInitString = null;
    try {
      setterInitString =
          new StringBuilder(pattern.getGetterInitString(callExp.toString()));
    } catch (NullPointerException e) {
      setterInitString = new StringBuilder("");
      setterInitString.append("EClass ").append(classifierName).append(" = ")
          .append("(EClass)").append(packageName).append(".eINSTANCE").append(
              ".getEClassifier(").append("\"").append(className).append("\")")
          .append(";").append("\n");

      setterInitString.append("EStructuralFeature ").append(featureName)
          .append(" = ").append(classifierName).append(".").append(
              "getEStructuralFeature(\"").append(propertyName).append("\")")
          .append(";\n");
    }

    // Генерируем строку для обращения к свойству
    StringBuilder setterReferenceString =
        new StringBuilder().append(reference).append(".eSet(").append(
            featureName).append(", ").append(REPLACE_CODE_STRING).append(");");

    String[] result = new String[importedStrings.size() + 2];
    result[0] = setterInitString.toString();
    result[1] = setterReferenceString.toString();
    int k = 2;
    for (int i = 0; i < importedStrings.size(); i++) {
      result[i + k] = importedStrings.get(i);
    }
    return result;

  }

  /**
   * создает и возвращает массив из двух или более строк: в первой - код,
   * определяющий переменную, во второй - код модификатора с выражением для
   * подстановки реального выражения, в третьей и далее строки, описывающие
   * импортируемые классы
   * 
   * @param v
   * @param ptrn
   * @return
   */
  private String[] createVariableSetterStrings(
      VariableExp<EClassifier, EParameter> v, final VariableExpCodeNode ptrn) {

    List<String> importedStrings = new ArrayList<String>();

    StringBuilder setterInitString = new StringBuilder();
    String varName = v.getName();

    EClass clazz = (EClass) v.getReferredVariable().getType();
    EFactory factory = clazz.getEPackage().getEFactoryInstance();

    String factoryName = factory.getClass().getSimpleName();
    for (Class<?> iface : factory.getClass().getInterfaces()) {
      if (factory.getClass().getPackage().getName().contains(
          iface.getPackage().getName())
          && factoryName.contains(iface.getSimpleName())) {

        String importString = "import " + iface.getName() + ";";
        importedStrings.add(importString);
        // ptrn.addImportString(importString);
        factoryName = iface.getSimpleName();
        break;
      }
    }

    setterInitString.append(clazz.getName()).append(" ").append(varName)
        ./* append(";").append(varName). */append(" = ").append(factoryName)
        .append(".eINSTANCE").append(".").append("create").append(
            clazz.getName()).append("();");

    StringBuilder setterCallString =
        new StringBuilder().append(varName).append(" = ").append(
            REPLACE_CODE_STRING).append(";");

    String[] result = new String[importedStrings.size() + 2];
    result[0] = setterInitString.toString();
    result[1] = setterCallString.toString();
    int k = 2;
    for (int i = 0; i < importedStrings.size(); i++) {
      result[i + k] = importedStrings.get(i);
    }
    return result;
  }

  /**
   * создает и возвращает массив из двух строк: в первой - код, инициализирующий
   * работу get-метода, во второй - код get-метода
   * 
   * @param v
   * @param ptrn
   * @return
   */
  private String[] createVariableGetterStrings(
      VariableExp<EClassifier, EParameter> v, final VariableExpCodeNode ptrn) {

    String result[] = new String[2];

    StringBuilder getterInitString = new StringBuilder("");
    String varName = v.getName();

    StringBuilder getterCallString = new StringBuilder(varName);
    result[0] = getterInitString.toString();
    result[1] = getterCallString.toString();
    return result;
  }
}
